package lore.soft.fullkeyboard.utils;

import android.view.KeyEvent;
import android.view.inputmethod.InputConnection;

/**
 * Created by lore on 11/18/16.
 */

public class KeyEventAscii {
    private int combinedCode;

    private final static int asciiToKeyCode[] = new int[127];
    private final static int KF_MASK = 0xffff;
    private final static int KF_SHIFTABLE = 0x10000;
    private final static int KF_SHIFTED = 0x20000;
    private final static int KF_LETTER = 0x40000;

    static {
        // Include RETURN in this set even though it's not printable.
        // Most other non-printable keys get handled elsewhere.
        asciiToKeyCode['\n'] = KeyEvent.KEYCODE_ENTER | KF_SHIFTABLE;

        // Non-alphanumeric ASCII codes which have their own keys
        // (on some keyboards)
        asciiToKeyCode[' '] = KeyEvent.KEYCODE_SPACE | KF_SHIFTABLE;

        asciiToKeyCode['`'] = KeyEvent.KEYCODE_GRAVE;
        asciiToKeyCode['~'] = KeyEvent.KEYCODE_GRAVE| KF_SHIFTED;

        asciiToKeyCode['1'] = KeyEvent.KEYCODE_1;
        asciiToKeyCode['!'] = KeyEvent.KEYCODE_1| KF_SHIFTED;
        asciiToKeyCode['2'] = KeyEvent.KEYCODE_2;
        asciiToKeyCode['@'] = KeyEvent.KEYCODE_AT;
        asciiToKeyCode['3'] = KeyEvent.KEYCODE_3;
        asciiToKeyCode['#'] = KeyEvent.KEYCODE_POUND;
        asciiToKeyCode['4'] = KeyEvent.KEYCODE_4;
        asciiToKeyCode['$'] = KeyEvent.KEYCODE_4| KF_SHIFTED;
        asciiToKeyCode['5'] = KeyEvent.KEYCODE_5;
        asciiToKeyCode['%'] = KeyEvent.KEYCODE_5| KF_SHIFTED;
        asciiToKeyCode['6'] = KeyEvent.KEYCODE_6;
        asciiToKeyCode['^'] = KeyEvent.KEYCODE_6| KF_SHIFTED;
        asciiToKeyCode['7'] = KeyEvent.KEYCODE_7;
        asciiToKeyCode['&'] = KeyEvent.KEYCODE_7| KF_SHIFTED;
        asciiToKeyCode['8'] = KeyEvent.KEYCODE_8;
        asciiToKeyCode['*'] = KeyEvent.KEYCODE_STAR;
        asciiToKeyCode['9'] = KeyEvent.KEYCODE_9;
        asciiToKeyCode['('] = KeyEvent.KEYCODE_9| KF_SHIFTED;
        asciiToKeyCode['0'] = KeyEvent.KEYCODE_0;
        asciiToKeyCode[')'] = KeyEvent.KEYCODE_0| KF_SHIFTED;

        asciiToKeyCode['-'] = KeyEvent.KEYCODE_MINUS;
        asciiToKeyCode['_'] = KeyEvent.KEYCODE_MINUS| KF_SHIFTED;
        asciiToKeyCode['='] = KeyEvent.KEYCODE_EQUALS;
        asciiToKeyCode['+'] = KeyEvent.KEYCODE_PLUS;

        asciiToKeyCode['['] = KeyEvent.KEYCODE_LEFT_BRACKET;
        asciiToKeyCode['{'] = KeyEvent.KEYCODE_LEFT_BRACKET| KF_SHIFTED;
        asciiToKeyCode[']'] = KeyEvent.KEYCODE_RIGHT_BRACKET;
        asciiToKeyCode['}'] = KeyEvent.KEYCODE_RIGHT_BRACKET| KF_SHIFTED;
        asciiToKeyCode['\\'] = KeyEvent.KEYCODE_BACKSLASH;
        asciiToKeyCode['|'] = KeyEvent.KEYCODE_BACKSLASH| KF_SHIFTED;

        asciiToKeyCode[';'] = KeyEvent.KEYCODE_SEMICOLON;
        asciiToKeyCode[':'] = KeyEvent.KEYCODE_SEMICOLON| KF_SHIFTED;
        asciiToKeyCode['\''] = KeyEvent.KEYCODE_APOSTROPHE;
        asciiToKeyCode['"'] = KeyEvent.KEYCODE_APOSTROPHE| KF_SHIFTED;

        asciiToKeyCode[','] = KeyEvent.KEYCODE_COMMA;
        asciiToKeyCode['<'] = KeyEvent.KEYCODE_COMMA| KF_SHIFTED;
        asciiToKeyCode['.'] = KeyEvent.KEYCODE_PERIOD;
        asciiToKeyCode['>'] = KeyEvent.KEYCODE_PERIOD| KF_SHIFTED;
        asciiToKeyCode['/'] = KeyEvent.KEYCODE_SLASH;
        asciiToKeyCode['?'] = KeyEvent.KEYCODE_SLASH| KF_SHIFTED;

        for (int i = 0; i <= 25; ++i) {
            asciiToKeyCode['a' + i] = KeyEvent.KEYCODE_A + i | KF_LETTER;
            asciiToKeyCode['A' + i] = KeyEvent.KEYCODE_A + i | KF_SHIFTED | KF_LETTER;
        }
    }

    public KeyEventAscii(int ascii) {
        combinedCode= asciiToKeyCode[ascii];
    }

    public int getCombinedCode() {
        return combinedCode;
    }

    public int getKeyCode() {
        return  combinedCode& KF_MASK;
    }

//    public boolean isShiftable() {
//        return (combinedCode & KF_SHIFTABLE) > 0;
//    }

    public boolean isShifted() {
        return (combinedCode & KF_SHIFTED) > 0;
    }

    public boolean isLetter() {
        return (combinedCode & KF_LETTER) > 0;
    }

    private int getMetaState(
            boolean shifted, boolean ctrled, boolean alted, boolean metaed) {
        int meta = 0;
        if (shifted) meta |= KeyEvent.META_SHIFT_ON | KeyEvent.META_SHIFT_LEFT_ON;
        if (ctrled) meta |= KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON;
        if (alted) meta |= KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON;
        if (metaed) meta |= KeyEvent.META_META_ON | KeyEvent.META_META_LEFT_ON;
        return meta;
    }

    public void sendKeyDown(InputConnection ic) {
        sendKeyDown(ic, isShifted(), false, false, false);
    }

    public void sendKeyUp(InputConnection ic) {
        sendKeyUp(ic, isShifted(), false, false, false);
    }

    public void sendKeyDown(InputConnection ic,
                            boolean ctrled, boolean alted, boolean metaed) {
        sendKey(ic, false, getMetaState(isShifted(), ctrled, alted, metaed));
    }

    public void sendKeyUp(InputConnection ic,
                          boolean ctrled, boolean alted, boolean metaed) {
        sendKey(ic, true, getMetaState(isShifted(), ctrled, alted, metaed));
    }

    public void sendKeyDown(InputConnection ic,
                             boolean shifted, boolean ctrled, boolean alted, boolean metaed) {
        sendKey(ic, false, getMetaState(shifted, ctrled, alted, metaed));
    }

    public void sendKeyUp(InputConnection ic,
                             boolean shifted, boolean ctrled, boolean alted, boolean metaed) {
        sendKey(ic, true, getMetaState(shifted, ctrled, alted, metaed));
    }

    private void sendKey(InputConnection ic, boolean upDown, int meta) {
        long now = System.currentTimeMillis();
        if (ic != null) ic.sendKeyEvent(new KeyEvent(
                now, now, upDown? KeyEvent.ACTION_UP: KeyEvent.ACTION_DOWN, getKeyCode(), 0, meta));
    }
}
